rbbcode 1.0.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3894f3d4d56f04b0c0d7ad5fa194eef37e9933f49993dc9ddeb243b4bdf1bda9
4
+ data.tar.gz: f0167cb15f14b2c37dbbd55205e4e3b08275f26b8d8c9d9e9a95c684741c451f
5
+ SHA512:
6
+ metadata.gz: 1a40401398844f12b7b874cedfc173bb98a887bc4dad718d2d30ece44b338060c84441e87265b12b91e777608a94b29f7eb9f074f49f810acb1f2dc1de9e01be
7
+ data.tar.gz: f0712fb419b27353fd569ce150adf74c47ef2d45cc6ed16dc80bcb6bd759354e4b3233596dfa942cda9845cb288d6a720b7a73dc2dff78b16538c17a8736b315
@@ -1,141 +1,286 @@
1
- class RbbCode
2
- module Attributes
3
- # Strips any number of double quotes from the beginning and end of the string
4
- def strip_quotes(str)
5
- str.sub(/^"+/, '').sub(/"+$/, '')
6
- end
7
- end
8
-
9
- module RecursiveConversion
10
- def recursively_convert(node, depth = 0)
11
- if node.terminal?
12
- if node.respond_to?(:to_html)
13
- node.to_html
14
- else
15
- node.text_value.match(/^[\r\n\t]+$/) ? '' : node.text_value
16
- end
17
- else
18
- if node.respond_to?(:to_html)
19
- node.to_html
20
- else
21
- node.elements.collect do |sub_node|
22
- recursively_convert(sub_node, depth + 1)
23
- end.join
24
- end
25
- end
26
- end
27
- end
28
-
29
- module DocumentNode
30
- def to_html
31
- contents.elements.collect { |p| p.to_html }.join
32
- end
33
- end
34
-
35
- module ParagraphNode
36
- include RecursiveConversion
37
-
38
- def to_html
39
- html = elements.collect do |node|
40
- recursively_convert(node)
41
- end.join
42
- "\n<p>" + html + "</p>\n"
43
- end
44
- end
45
-
46
- module BlockquoteNode
47
- include RecursiveConversion
48
-
49
- def to_html
50
- "\n<blockquote>" + recursively_convert(contents) + "</blockquote>\n"
51
- end
52
- end
53
-
54
- module ListNode
55
- include RecursiveConversion
56
-
57
- def to_html
58
- "\n<ul>" + recursively_convert(contents) + "</ul>\n"
59
- end
60
- end
61
-
62
- module ListItemNode
63
- include RecursiveConversion
64
-
65
- def to_html
66
- "\n<li>" + recursively_convert(contents) + "</li>\n"
67
- end
68
- end
69
-
70
- module URLTagNode
71
- include Attributes
72
-
73
- def url_to_html
74
- if respond_to?(:url) and respond_to?(:text)
75
- # A URL tag formatted like [url=http://example.com]Example[/url]
76
- '<a href="' + strip_quotes(url.text_value) + '">' + text.text_value + '</a>'
77
- else
78
- # A URL tag formatted like [url]http://example.com[/url]
79
- '<a href="' + inner_bbcode + '">' + inner_bbcode + '</a>'
80
- end
81
- end
82
- end
83
-
84
- module ImgTagNode
85
- def img_to_html
86
- '<img src="' + inner_bbcode + '" alt="Image"/>'
87
- end
88
- end
89
-
90
- module TagNode
91
- include RecursiveConversion
92
-
93
- TAG_MAPPINGS = {'b' => 'strong', 'i' => 'em', 'u' => 'u', 'url' => URLTagNode, 'img' => ImgTagNode}
94
-
95
- def contents
96
- # The first element is the opening tag, the second is everything inside,
97
- # and the third is the closing tag.
98
- elements[1]
99
- end
100
-
101
- def tag_name
102
- elements.first.text_value.slice(1..-2).downcase
103
- end
104
-
105
- def inner_bbcode
106
- contents.elements.collect { |e| e.text_value }.join
107
- end
108
-
109
- def inner_html
110
- contents.elements.collect do |node|
111
- recursively_convert(node)
112
- end.join
113
- end
114
-
115
- def to_html
116
- t = TAG_MAPPINGS[tag_name]
117
- if t.nil?
118
- raise "No tag mapping found for #{tag_name}"
119
- elsif t.is_a?(Module)
120
- extend(t)
121
- send(tag_name + '_to_html')
122
- # Thus, if our tag_name is"url, and TAG_MAPPINGS points us to URLTagNode,
123
- # that module must define url_to_html.
124
- else
125
- "<#{t}>" + inner_html + "</#{t}>"
126
- end
127
- end
128
- end
129
-
130
- module SingleBreakNode
131
- def to_html
132
- '<br/>'
133
- end
134
- end
135
-
136
- module LiteralTextNode
137
- def to_html
138
- text_value
139
- end
140
- end
1
+ class RbbCode
2
+ module Attributes
3
+ # Strips any number of double quotes from the beginning and end of the string.
4
+ def strip_quotes(str)
5
+ str.sub(/^"+/, '').sub(/"+$/, '')
6
+ end
7
+ end
8
+
9
+ module RecursiveConversion
10
+ def recursively_convert(node, output_method, options, depth = 0)
11
+ if node.terminal?
12
+ if node.respond_to?(output_method)
13
+ # This is a terminal node with a custom implementation of the output
14
+ # method (e.g. #to_html).
15
+ node.send(output_method)
16
+ else
17
+ # This is a terminal node without a custom implementation of the
18
+ # output method. If the node consists solely of whitespace, emit the
19
+ # empty string. Otherwise, emit the node's text value.
20
+ node.text_value.match(/\A[\n\t]+\Z/) ? '' : node.text_value
21
+ end
22
+ else
23
+ if node.respond_to?(output_method)
24
+ # This is a non-terminal node with a custom implementation of the
25
+ # output method.
26
+ node.send(output_method, options)
27
+ else
28
+ # This is a non-terminal node without a custom implementation of the
29
+ # output method. Convert all its child nodes and concatenate the results.
30
+ node.elements.collect do |sub_node|
31
+ recursively_convert(sub_node, output_method, options, depth + 1)
32
+ end.join
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module DocumentNode
39
+ def to_html(options)
40
+ contents.elements.collect { |p| p.to_html(options) }.join
41
+ end
42
+
43
+ def to_markdown(options)
44
+ contents.elements.collect { |p| p.to_markdown(options) }.join
45
+ end
46
+ end
47
+
48
+ module ParagraphNode
49
+ include RecursiveConversion
50
+
51
+ def to_html(options)
52
+ # Convert all child nodes, concatenate the results,
53
+ # and wrap the concatenated HTML in <p> tags.
54
+ html = elements.collect do |node|
55
+ recursively_convert(node, :to_html, options)
56
+ end.join
57
+ "\n<p>" + html + "</p>\n"
58
+ end
59
+
60
+ def to_markdown(options)
61
+ # Convert all child nodes, concatenate the results,
62
+ # and append newline characters.
63
+ markdown = elements.collect do |node|
64
+ recursively_convert(node, :to_markdown, options)
65
+ end.join
66
+ markdown + "\n\n"
67
+ end
68
+ end
69
+
70
+ module BlockquoteNode
71
+ include RecursiveConversion
72
+
73
+ def to_html(options)
74
+ # Detect paragraph breaks and wrap the result in <blockquote> tags.
75
+ paragraphs = []
76
+ cur_para = ''
77
+ lines.elements.each do |line|
78
+ inner = recursively_convert(line, :to_html, options)
79
+ unless inner.blank?
80
+ cur_para << inner
81
+ if line.post_breaks == 1
82
+ cur_para << ' '
83
+ elsif line.post_breaks >= 2
84
+ paragraphs << cur_para
85
+ cur_para = ''
86
+ end
87
+ end
88
+ end
89
+ unless cur_para.blank?
90
+ paragraphs << cur_para
91
+ end
92
+ inner = paragraphs.map { |str| "<p>#{str}</p>" }.join("\n")
93
+ "\n<blockquote>" + inner + "</blockquote>\n"
94
+ end
95
+
96
+ def to_markdown(options)
97
+ # Add a > character per line, preserving linebreaks as they are in the source.
98
+ # Then append two newlines.
99
+ '> ' + lines.elements.inject('') do |output, line|
100
+ inner_markdown = recursively_convert(line.contents, :to_markdown, options)
101
+ output + inner_markdown + ("\n> " * line.post_breaks)
102
+ end + "\n\n"
103
+ end
104
+ end
105
+
106
+ module BlockquoteLineNode
107
+ # Returns the number of line breaks after this line. May be zero for the final
108
+ # line, since there doesn't have to be a break before [/quote].
109
+ def post_breaks
110
+ breaks.elements.length
111
+ end
112
+ end
113
+
114
+ module ListNode
115
+ include RecursiveConversion
116
+
117
+ def to_html(options)
118
+ # Convert the :contents child node (defined in the .treetop file)
119
+ # and wrap the result in <ul> tags.
120
+ "\n<ul>" + recursively_convert(items, :to_html, options) + "</ul>\n"
121
+ end
122
+
123
+ def to_markdown(options)
124
+ # Convert the :contents child node (defined in the .treetop file).
125
+ # (Unlike with HTML, no outer markup needed.) Then append an extra
126
+ # newline, for a total of two at the end.
127
+ recursively_convert(items, :to_markdown, options) + "\n"
128
+ end
129
+ end
130
+
131
+ module ListItemNode
132
+ include RecursiveConversion
133
+
134
+ def to_html(options)
135
+ # Convert the :contents child node (defined in the .treetop file)
136
+ # and wrap the result in <li> tags.
137
+ "\n<li>" + recursively_convert(contents, :to_html, options) + "</li>\n"
138
+ end
139
+
140
+ def to_markdown(options)
141
+ # Convert the :contents child node (defined in the .treetop file)
142
+ # and add * characters.
143
+ "* " + recursively_convert(contents, :to_html, options) + "\n"
144
+ end
145
+ end
146
+
147
+ # You won't find this module in the .treetop file. Instead, it's effectively a specialization
148
+ # of TagNode, which calls to ImgTagNode when processing an img tag. (However, one of the
149
+ # child nodes used here, :url, is indeed defined in the .treetop file.)
150
+ module URLTagNode
151
+ include Attributes
152
+
153
+ def url_to_html(options)
154
+ # The :url child node (defined in the .treetop file) may or may not exist,
155
+ # depending on how the link is formatted in the BBCode source.
156
+ if respond_to?(:url) and respond_to?(:text)
157
+ # This is a URL tag formatted like [url=http://example.com]Example[/url].
158
+ '<a href="' + strip_quotes(url.text_value) + '">' + text.text_value + '</a>'
159
+ else
160
+ # This is a URL tag formatted like [url]http://example.com[/url].
161
+ '<a href="' + inner_bbcode + '">' + inner_bbcode + '</a>'
162
+ end
163
+ end
164
+
165
+ def url_to_markdown(options)
166
+ if respond_to?(:url) and respond_to?(:text)
167
+ # This is a URL tag formatted like [url=http://example.com]Example[/url].
168
+ '[' + text.text_value + '](' + strip_quotes(url.text_value) + ')'
169
+ else
170
+ # This is a URL tag formatted like [url]http://example.com[/url].
171
+ '[' + inner_bbcode + '](' + inner_bbcode + ')'
172
+ end
173
+ end
174
+ end
175
+
176
+ # You won't find this module in the .treetop file. Instead, it's effectively a specialization
177
+ # of TagNode, which calls to ImgTagNode when processing an img tag.
178
+ module ImgTagNode
179
+ def img_to_html(options)
180
+ '<img src="' + inner_bbcode + '" alt="Image"/>'
181
+ end
182
+
183
+ def img_to_markdown(options)
184
+ "![Image](#{inner_bbcode})"
185
+ end
186
+ end
187
+
188
+ module UTagNode
189
+ def u_to_markdown(options)
190
+ # Underlining is unsupported in Markdown. So we just ignore [u] tags.
191
+ inner_bbcode
192
+ end
193
+ end
194
+
195
+ module TagNode
196
+ include RecursiveConversion
197
+
198
+ # For each tag name, we can either: (a) map to a simple HTML tag or Markdown character, or
199
+ # (b) invoke a separate Ruby module for more advanced logic.
200
+ TAG_MAPPINGS = {
201
+ html: {'b' => 'strong', 'i' => 'em', 'u' => 'u', 'url' => URLTagNode, 'img' => ImgTagNode},
202
+ markdown: {'b' => '**', 'i' => '*', 'u' => UTagNode, 'url' => URLTagNode, 'img' => ImgTagNode}
203
+ }
204
+
205
+ def contents
206
+ # The first element is the opening tag, the second is everything inside,
207
+ # and the third is the closing tag.
208
+ elements[1]
209
+ end
210
+
211
+ def tag_name
212
+ elements.first.text_value.slice(1..-2).downcase
213
+ end
214
+
215
+ def inner_bbcode
216
+ contents.elements.collect { |e| e.text_value }.join
217
+ end
218
+
219
+ def inner_html(options)
220
+ contents.elements.collect do |node|
221
+ recursively_convert(node, :to_html, options)
222
+ end.join
223
+ end
224
+
225
+ def inner_markdown(options)
226
+ contents.elements.collect do |node|
227
+ recursively_convert(node, :to_markdown, options)
228
+ end.join
229
+ end
230
+
231
+ def wrap_html(t, options)
232
+ "<#{t}>" + inner_html(options) + "</#{t}>"
233
+ end
234
+
235
+ def wrap_markdown(t, options)
236
+ t + inner_markdown(options) + t
237
+ end
238
+
239
+ def convert(output_format, options)
240
+ # Consult TAG_MAPPINGS to decide how to process this type of tag.
241
+ t = TAG_MAPPINGS[output_format][tag_name]
242
+ if t.nil?
243
+ raise "No tag mapping found for #{tag_name}"
244
+ elsif t.is_a?(Module)
245
+ # This type of tag requires more than just a simple mapping from one tag name
246
+ # to another. So we invoke a separate Ruby module.
247
+ extend(t)
248
+ send("#{tag_name}_to_#{output_format}", options)
249
+ # Thus, if our tag_name is"url, and TAG_MAPPINGS points us to URLTagNode,
250
+ # that module must define url_to_html.
251
+ else
252
+ # For this type of tag, a simple mapping from the tag name to a string (such as
253
+ # <i>) suffices.
254
+ send("wrap_#{output_format}", t, options)
255
+ end
256
+ end
257
+
258
+ def to_html(options)
259
+ convert :html, options
260
+ end
261
+
262
+ def to_markdown(options)
263
+ convert :markdown, options
264
+ end
265
+ end
266
+
267
+ module SingleBreakNode
268
+ def to_html(options)
269
+ '<br/>'
270
+ end
271
+
272
+ def to_markdown(options)
273
+ "\n"
274
+ end
275
+ end
276
+
277
+ module LiteralTextNode
278
+ def to_html(options)
279
+ text_value
280
+ end
281
+
282
+ def to_markdown(options)
283
+ text_value
284
+ end
285
+ end
141
286
  end
@@ -1,113 +1,110 @@
1
- <%
2
- def def_tag(rule_name, tag_name)
3
- "
4
- rule #{rule_name}
5
- ('[#{tag_name.downcase}]'/'[#{tag_name.upcase}]')
6
- (!'[/#{tag_name.downcase}]' !'[/#{tag_name.upcase}]'
7
- (tag <RbbCode::TagNode> / .))+
8
- ('[/#{tag_name.downcase}]' / '[/#{tag_name.upcase}]')
9
- end
10
- "
11
- end
12
- %>
13
-
14
- grammar RbbCodeGrammar
15
- rule document
16
- # Consume the trailing linebreaks, because the paragraph lookahead
17
- # doesn't consume them.
18
- contents:(blockquote <RbbCode::BlockquoteNode> / list <RbbCode::ListNode> / paragraph <RbbCode::ParagraphNode> / . <RbbCode::LiteralTextNode>)* break_ws* <RbbCode::DocumentNode>
19
- end
20
-
21
- rule paragraph
22
- (break_ws 2..)
23
- (
24
- !(break_ws 2..)
25
- paragraph_contents
26
- )+
27
- end
28
-
29
- rule paragraph_contents
30
- (tag <RbbCode::TagNode> / single_break_ws / .)
31
- end
32
-
33
- rule break_ws
34
- # Allow whitespace around the linebreaks
35
- [ \t]* [\r\n] [ \t]*
36
- end
37
-
38
- rule whitespace
39
- # Any whitespace, including linebreaks
40
- [ \t\r\n]
41
- end
42
-
43
- rule single_break_ws
44
- # We don't count linebreaks when they're immediately followed by
45
- # certain keywords. This avoids printing an extra <br/> in some cases.
46
- break_ws !break_ws !(break_ws* ('[/quote]' / '[*]' / '[/list]')) <RbbCode::SingleBreakNode>
47
- end
48
-
49
- rule blockquote
50
- break_ws*
51
- '[quote]'
52
- contents:(
53
- # Possible linebreaks after opening quote tag
54
- break_ws*
55
-
56
- # First paragraph (mandatory)
57
- (blockquote_paragraph <RbbCode::ParagraphNode>)
58
-
59
- # Subsequent paragraphs (optional)
60
- (
61
- (break_ws 2..)
62
- (blockquote_paragraph <RbbCode::ParagraphNode>)
63
- )*
64
-
65
- # Possible linebreaks before closing quote tag
66
- break_ws*
67
- )
68
- '[/quote]'
69
- end
70
-
71
- rule blockquote_paragraph
72
- (!('[/quote]' / (break_ws 2..)) paragraph_contents)+
73
- end
74
-
75
- rule list
76
- break_ws*
77
- '[list]'
78
- contents:(
79
- # Possible linebreaks after opening list tag
80
- whitespace*
81
-
82
- # At least one list item
83
- (
84
- (
85
- '[*]'
86
- contents:(!'[/list]' !'[*]' paragraph_contents)*
87
- <RbbCode::ListItemNode>
88
- )
89
- )+
90
-
91
- # Possible linebreaks before closing list tag
92
- whitespace*
93
- )
94
- '[/list]'
95
- end
96
-
97
- rule tag
98
- # Make sure that anytime you call def_tag, you add it to this list:
99
- bold / italic / underline / simple_url / complex_url / img
100
- end
101
-
102
- <%= def_tag 'bold', 'b' %>
103
- <%= def_tag 'italic', 'i' %>
104
- <%= def_tag 'underline', 'u' %>
105
- <%= def_tag 'simple_url', 'url' %>
106
- <%= def_tag 'img', 'img' %>
107
-
108
- rule complex_url
109
- '[url=' url:[^\]]+ ']'
110
- text:(!'[/url]' .)+
111
- '[/url]'
112
- end
1
+ <%
2
+ def def_tag(rule_name, tag_name)
3
+ "
4
+ rule #{rule_name}
5
+ # Opening tag
6
+ (
7
+ '[#{tag_name.downcase}]' /
8
+ '[#{tag_name.upcase}]'
9
+ )
10
+ # Inner BBCode (which may include nested tags)
11
+ (
12
+ !'[/#{tag_name.downcase}]'
13
+ !'[/#{tag_name.upcase}]'
14
+ (tag / .)
15
+ )+
16
+ # Closing tag
17
+ (
18
+ '[/#{tag_name.downcase}]' /
19
+ '[/#{tag_name.upcase}]'
20
+ )
21
+ end
22
+ "
23
+ end
24
+ %>
25
+
26
+ grammar RbbCodeGrammar
27
+ rule document
28
+ contents:(blockquote / list / paragraph / literal_text)*
29
+ break_ws*
30
+ <RbbCode::DocumentNode>
31
+ end
32
+
33
+ rule literal_text
34
+ [^\n]+ <RbbCode::LiteralTextNode>
35
+ end
36
+
37
+ rule paragraph
38
+ (break_ws 2..)
39
+ (
40
+ !(break_ws 2..)
41
+ (tag / single_break_ws / .)
42
+ )+
43
+ <RbbCode::ParagraphNode>
44
+ end
45
+
46
+ rule break_ws
47
+ # A linebreak, possibly surrounded by whitespace
48
+ [ \t]* "\n" [ \t]*
49
+ end
50
+
51
+ rule single_break_ws
52
+ # We don't count linebreaks when they're immediately followed by
53
+ # certain keywords. This avoids printing an extra <br/> in some cases.
54
+ break_ws !break_ws !(break_ws* ('[/quote]' / '[*]' / '[/list]')) <RbbCode::SingleBreakNode>
55
+ end
56
+
57
+ rule blockquote
58
+ break_ws*
59
+ '[quote]'
60
+ "\n"*
61
+ lines:blockquote_line*
62
+ '[/quote]'
63
+ <RbbCode::BlockquoteNode>
64
+ end
65
+
66
+ rule blockquote_line
67
+ contents:(!('[/quote]' / "\n") (tag / .))+
68
+ [ \t]*
69
+ breaks:break_ws*
70
+ <RbbCode::BlockquoteLineNode>
71
+ end
72
+
73
+ rule list
74
+ break_ws*
75
+ '[list]'
76
+ [ \t\n]*
77
+ items:list_item*
78
+ [ \t\n]*
79
+ '[/list]'
80
+ <RbbCode::ListNode>
81
+ end
82
+
83
+ rule list_item
84
+ '[*]'
85
+ [ \t]*
86
+ contents:(
87
+ !'[/list]' !'[*]'
88
+ (tag / single_break_ws / .)
89
+ )*
90
+ <RbbCode::ListItemNode>
91
+ end
92
+
93
+ rule tag
94
+ # Make sure that anytime you call def_tag, you add it to this list:
95
+ (bold / italic / underline / simple_url / complex_url / img)
96
+ <RbbCode::TagNode>
97
+ end
98
+
99
+ <%= def_tag 'bold', 'b' %>
100
+ <%= def_tag 'italic', 'i' %>
101
+ <%= def_tag 'underline', 'u' %>
102
+ <%= def_tag 'simple_url', 'url' %>
103
+ <%= def_tag 'img', 'img' %>
104
+
105
+ rule complex_url
106
+ '[url=' url:[^\]]+ ']'
107
+ text:(!'[/url]' .)+
108
+ '[/url]'
109
+ end
113
110
  end
@@ -1,13 +1,13 @@
1
- class RbbCode
2
- DEFAULT_SANITIZE_CONFIG = {
3
- :elements => %w[a blockquote br code del em img li p pre strong ul u],
4
- :attributes => {
5
- 'a' => %w[href target],
6
- 'img' => %w[alt src]
7
- },
8
-
9
- :protocols => {
10
- 'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]}
11
- }
12
- }
1
+ class RbbCode
2
+ DEFAULT_SANITIZE_CONFIG = {
3
+ :elements => %w[a blockquote br code del em img li p pre strong ul u],
4
+ :attributes => {
5
+ 'a' => %w[href target],
6
+ 'img' => %w[alt src]
7
+ },
8
+
9
+ :protocols => {
10
+ 'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]}
11
+ }
12
+ }
13
13
  end
data/lib/rbbcode.rb CHANGED
@@ -1,50 +1,72 @@
1
- # Uncomment this when developing:
2
- #$:.unshift './lib'
3
-
4
- require 'erb'
5
- require 'rubygems'
6
- require 'treetop'
7
- require 'sanitize'
8
- require 'rbbcode/node_extensions'
9
- require 'rbbcode/sanitize'
10
-
11
- class RbbCode
12
- def self.parser_class
13
- if !@grammar_loaded
14
- Treetop.load_from_string(
15
- ERB.new(
16
- File.read(
17
- File.join(
18
- File.dirname(__FILE__),
19
- 'rbbcode/rbbcode_grammar.treetop'
20
- )
21
- )
22
- ).result
23
- )
24
- @grammar_loaded = true
25
- end
26
- RbbCodeGrammarParser
27
- end
28
-
29
- def initialize(options = {})
30
- @options = {
31
- :sanitize => true,
32
- :sanitize_config => RbbCode::DEFAULT_SANITIZE_CONFIG
33
- }.merge(options)
34
- end
35
-
36
- def convert(bb_code)
37
- html = self.class.parser_class.new.parse("\n\n" + bb_code + "\n\n").to_html
38
- if @options[:emoticons]
39
- @options[:emoticons].each do |emoticon, url|
40
- html.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>')
41
- end
42
- end
43
- html
44
- if @options[:sanitize]
45
- Sanitize.clean(html, @options[:sanitize_config])
46
- else
47
- html
48
- end
49
- end
1
+ # Uncomment this when developing:
2
+ #$:.unshift './lib'
3
+
4
+ require 'erb'
5
+ require 'rubygems'
6
+ require 'treetop'
7
+ require 'sanitize'
8
+ require 'rbbcode/node_extensions'
9
+ require 'rbbcode/sanitize'
10
+
11
+ class RbbCode
12
+ def self.parser_class
13
+ if !instance_variable_defined?(:@grammar_loaded) or !@grammar_loaded
14
+ Treetop.load_from_string(
15
+ ERB.new(
16
+ File.read(
17
+ File.join(
18
+ File.dirname(__FILE__),
19
+ 'rbbcode/rbbcode_grammar.treetop'
20
+ )
21
+ )
22
+ ).result
23
+ )
24
+ @grammar_loaded = true
25
+ end
26
+ RbbCodeGrammarParser
27
+ end
28
+
29
+ def initialize(options = {})
30
+ @options = {
31
+ :output_format => :html,
32
+ :emoticons => false,
33
+ :sanitize => true,
34
+ :sanitize_config => RbbCode::DEFAULT_SANITIZE_CONFIG
35
+ }.merge(options)
36
+ end
37
+
38
+ def convert(bb_code, options = {})
39
+ # Options passed to #convert will override any options passed to .new.
40
+ options = @options.merge(options)
41
+ output_format = options.delete(:output_format)
42
+ emoticons = options.delete(:emoticons)
43
+ sanitize = options.delete(:sanitize)
44
+ sanitize_config = options.delete(:sanitize_config)
45
+
46
+ # Collapse CRLFs to LFs. Then replace any solitary CRs with LFs.
47
+ bb_code = bb_code.gsub("\r\n", "\n").gsub("\r", "\n")
48
+ # Add linebreaks before and after so that paragraphs etc. can be recognized.
49
+ bb_code = "\n\n" + bb_code + "\n\n"
50
+ output = self.class.parser_class.new.parse(bb_code).send("to_#{output_format}", options)
51
+ if emoticons
52
+ output = convert_emoticons(output)
53
+ end
54
+ # Sanitization works for HTML only.
55
+ if output_format == :html and sanitize
56
+ Sanitize.clean(output, sanitize_config)
57
+ else
58
+ output
59
+ end
60
+ end
61
+
62
+ def convert_emoticons(output)
63
+ @options[:emoticons].each do |emoticon, url|
64
+ output.gsub!(emoticon, '<img src="' + url + '" alt="Emoticon"/>')
65
+ end
66
+ output
67
+ end
68
+
69
+ def output_format
70
+ @options[:output_format]
71
+ end
50
72
  end
metadata CHANGED
@@ -1,116 +1,138 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbcode
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
5
- prerelease:
4
+ version: 1.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jarrett Colby
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-10-27 00:00:00.000000000 Z
11
+ date: 2023-07-06 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: treetop
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '='
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: 1.5.3
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '='
28
25
  - !ruby/object:Gem::Version
29
- version: '0'
26
+ version: 1.5.3
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: sanitize
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: '0'
33
+ version: '6'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 6.0.2
38
37
  type: :runtime
39
38
  prerelease: false
40
39
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
40
  requirements:
43
- - - ! '>='
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '6'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 6.0.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
44
59
  - !ruby/object:Gem::Version
45
- version: '0'
60
+ version: '5'
61
+ - !ruby/object:Gem::Dependency
62
+ name: minitest-reporters
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1'
46
75
  - !ruby/object:Gem::Dependency
47
76
  name: lorax
48
77
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
78
  requirements:
51
- - - ! '>='
79
+ - - '='
52
80
  - !ruby/object:Gem::Version
53
- version: '0'
81
+ version: 0.3.0.rc2
54
82
  type: :development
55
83
  prerelease: false
56
84
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
85
  requirements:
59
- - - ! '>='
86
+ - - '='
87
+ - !ruby/object:Gem::Version
88
+ version: 0.3.0.rc2
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
60
94
  - !ruby/object:Gem::Version
61
- version: '0'
95
+ version: '13'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13'
62
103
  description: Converts BBCode to HTML. Gracefully handles invalid input.
63
104
  email: jarrett@madebyhq.com
64
105
  executables: []
65
106
  extensions: []
66
107
  extra_rdoc_files: []
67
108
  files:
109
+ - lib/rbbcode.rb
68
110
  - lib/rbbcode/node_extensions.rb
69
111
  - lib/rbbcode/rbbcode_grammar.treetop
70
112
  - lib/rbbcode/sanitize.rb
71
- - lib/rbbcode.rb
72
113
  homepage: https://github.com/jarrett/rbbcode
73
114
  licenses: []
74
- post_install_message: ! '
75
-
76
- Important notice for users of 0.1.11 or lower
77
-
78
- =============================================
79
-
80
-
81
- RbbCode has been updated! The new release (1.x.x)
82
-
83
- is not compatible with the old one (0.1.11). If
84
-
85
- you want to upgrade to 1.x.x, you''ll need to
86
-
87
- adjust any calls to RbbCode in your code to match
88
-
89
- the new API. For more information:
90
-
91
-
92
- https://github.com/jarrett/rbbcode
93
-
94
- '
115
+ metadata: {}
116
+ post_install_message: "\r\nImportant notice for users of 0.1.11 or lower\r\n=============================================\r\n\r\nRbbCode
117
+ has been updated! The new release (1.x.x)\r\nis not compatible with the old one
118
+ (0.1.11). If\r\nyou want to upgrade to 1.x.x, you'll need to\r\nadjust any calls
119
+ to RbbCode in your code to match\r\nthe new API. For more information:\r\n\r\nhttps://github.com/jarrett/rbbcode\r\n"
95
120
  rdoc_options: []
96
121
  require_paths:
97
122
  - lib
98
123
  required_ruby_version: !ruby/object:Gem::Requirement
99
- none: false
100
124
  requirements:
101
- - - ! '>='
125
+ - - ">="
102
126
  - !ruby/object:Gem::Version
103
127
  version: '0'
104
128
  required_rubygems_version: !ruby/object:Gem::Requirement
105
- none: false
106
129
  requirements:
107
- - - ! '>='
130
+ - - ">="
108
131
  - !ruby/object:Gem::Version
109
132
  version: '0'
110
133
  requirements: []
111
- rubyforge_project:
112
- rubygems_version: 1.8.25
134
+ rubygems_version: 3.1.2
113
135
  signing_key:
114
- specification_version: 3
136
+ specification_version: 4
115
137
  summary: RbbCode
116
138
  test_files: []